今天來做 Training 頁面
先擺上今天的成果
預設主題 | 黑暗主題 |
---|---|
@Preview
@Composable
fun PrevTrainingScreen() {
KTheme(darkTheme = true) { //darkTheme true & false
TrainingScreen()
}
}
darkTheme
可以設定預覽主題效果用Scaffold
來佈局,上有 topBar
下有 floatingActionButton
。
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = { /* 日期 */ },
navigationIcon = { Icon(Icons.Filled.ArrowBack, contentDescription = "Back") }
)
},
floatingActionButton = {
FloatingActionButton(onClick = {/* 新增訓練或休息 */ }) {
Icon(Icons.Filled.Add, contentDescription = "Add Training")
}
}
) { padding ->
//訓練列表
}
topBar
:這次使用 CenterAlignedTopAppBar
可以讓標題置中,title
放入日期並且有 formatter
過,navigationIcon
用 material icon 的 ArrowBack
。floatingActionButton
:和主頁面同樣式,onClick 需要處理新增的選單是要新增訓練還是休。Scaffold(
//...
) { padding ->
LazyColumn(
modifier = Modifier
.padding(padding)
.padding(horizontal = 8.dp)
) {
items(trainingList) {
TrainingItem(it, onClick = {/*TODO*/ })
}
}
}
LazyColumn
:modifier
先將Scaffold
的padding
加入,再設定左右邊距離為 8.dp
items
:帶入資料trainingList
,把一組訓練的 Composable
提成TrainingItem
。這是放置一組訓練的資訊卡。 需要監聽點擊功能,所以在
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingItem(
trainingItem: TrainingItem,
onClick: () -> Unit
) {
Card(
onClick = onClick,
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp),
colors = CardDefaults.cardColors(
containerColor = if (trainingItem.isRest) {
MaterialTheme.colorScheme.tertiaryContainer
} else {
MaterialTheme.colorScheme.primaryContainer
}
),
shape = RoundedCornerShape(15)
) {
}
}
onClick
:監聽點擊事件,由TrainingItem
傳入的方法作為參數,因為 onClick
的卡片還是實驗性質的,所以要加上 @OptIn(ExperimentalMaterial3Api::class)
modifier
:先將卡片範圍展到最寬,再設置和下方卡片距離4.dp
colors
:複製Card
預設顏色參數,更改背景色,為了能響應黑色模式,採用MaterialTheme.colorScheme
的顏色shape
:想要圓角效果可以使用 RoundedCornerShape
,CircleShape
其實就是 RoundedCornerShape(50)
Card(
//...
) {
Row(
modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
) { //... }
}
modifier
:撐開上下 vertical = 16.dp
左右 horizontal = 16.dp
horizontalArrangement
:採用Arrangement.SpaceBetween
想要呈現平均分配的樣式。Row(
//...
) {
if (trainingItem.isRest) {
} else {
}
}
if (trainingItem.isRest) {
Box(modifier = Modifier.weight(3f))
Text(
text = "休息",
modifier = Modifier.weight(1f),
textAlign = TextAlign.Right,
color = MaterialTheme.colorScheme.onTertiaryContainer
)
Text(
text = "${trainingItem.restSecond} s",
modifier = Modifier.weight(1f),
textAlign = TextAlign.Right,
color = MaterialTheme.colorScheme.onTertiaryContainer
)
} else {
Text("${trainingItem.title}", modifier = Modifier.weight(1f))
Text(
text = "${trainingItem.weight} ${trainingItem.weightUnit.name} ",
modifier = Modifier.weight(1f)
)
Text(
text = "${trainingItem.times} ${trainingItem.timesUnit.name} ",
modifier = Modifier.weight(1f)
)
Box(modifier = Modifier.weight(2f))
}
Modifier.weight(3f)
來分配比例。textAlign = TextAlign.Right
為了讓 Text 看起來佔滿版面,並且與訓練內容做區分,靠右側對齊。MaterialTheme.colorScheme.onTertiaryContainer
,對應Container
這裡用了 onContainer
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingScreen() {
var date = LocalDateTime.now()
//假資料
var trainingList = listOf<TrainingItem>(
TrainingItem(
index = 1,
title = "深蹲",
weight = 30,
times = 15,
),
TrainingItem(
index = 2,
title = "深蹲",
weight = 30,
times = 15,
),
TrainingItem(
index = 3,
title = "深蹲",
weight = 30,
times = 15,
),
TrainingItem(
index = 4,
isRest = true,
restSecond = 30
),
)
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
date.format(formatter),
modifier = Modifier.clickable { /*TODO*/ })
},
navigationIcon = { Icon(Icons.Filled.ArrowBack, contentDescription = "Back") }
)
},
floatingActionButton = {
FloatingActionButton(onClick = {/*TODO*/ }) {
Icon(Icons.Filled.Add, contentDescription = "Add Training")
}
}
) { padding ->
LazyColumn(
modifier = Modifier
.padding(padding)
.padding(horizontal = 8.dp)
) {
items(trainingList) {
TrainingItem(it, onClick = {/*TODO*/ })
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingItem(
trainingItem: TrainingItem,
onClick: () -> Unit
) {
Card(
onClick = onClick,
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp),
colors = CardDefaults.cardColors(
containerColor = if (trainingItem.isRest) {
MaterialTheme.colorScheme.tertiaryContainer
} else {
MaterialTheme.colorScheme.primaryContainer
}
),
shape = RoundedCornerShape(15)
) {
Row(
modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
) {
if (trainingItem.isRest) {
Box(modifier = Modifier.weight(3f))
Text(
text = "休息",
modifier = Modifier.weight(1f),
textAlign = TextAlign.Right,
color = MaterialTheme.colorScheme.onTertiaryContainer
)
Text(
text = "${trainingItem.restSecond} s",
modifier = Modifier.weight(1f),
textAlign = TextAlign.Right,
color = MaterialTheme.colorScheme.onTertiaryContainer
)
} else {
Text("${trainingItem.title}", modifier = Modifier.weight(1f))
Text(
text = "${trainingItem.weight} ${trainingItem.weightUnit.name} ",
modifier = Modifier.weight(1f)
)
Text(
text = "${trainingItem.times} ${trainingItem.timesUnit.name} ",
modifier = Modifier.weight(1f)
)
Box(modifier = Modifier.weight(2f))
}
}
}
}
今日訓練
拳擊 50分鐘